import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Updates;
import io.netty.buffer.*;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelDuplexHandler;
import static com.mongodb.client.model.Filters.eq;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import org.bson.conversions.Bson;
import org.json.*;
import com.mongodb.client.MongoCollection;
import org.bson.Document;

public class DaemonServerHandler extends ChannelDuplexHandler {
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        IdleStateEvent e = (IdleStateEvent) evt;
        if (e.state() == IdleState.READER_IDLE) {
            setClientStatus(ctx, "Unknown");
            ctx.close();
        } else if (e.state() == IdleState.WRITER_IDLE) {
            JSONObject jsonObj = new JSONObject();
            jsonObj.put("id", "HeartBeatReqS2C");
            String response = jsonObj.toString();
            ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("id", "UploadInfoS2C");
        String result = jsonObj.toString();
        ctx.writeAndFlush(Unpooled.copiedBuffer(result.getBytes()));
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        setClientStatus(ctx, "Unknown");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object request) throws Exception {
        //发送的应该是{id:xxxx, name:xx, host:xx, port:xx, status:normal}，这里的port必须是数字，否则解析会错误
        //比如：{id:UploadInfoC2S, name:xx, host:xx, port:xx, status:normal}
        try {
            ByteBuf in = (ByteBuf) request;
            String data = in.toString(CharsetUtil.UTF_8);
            JSONObject jsonObj = new JSONObject(data);
            String msgId = jsonObj.getString("id");
            System.out.println("收到消息，id：" + msgId);

            String name = "";
            MongoCollection<Document> collection;
            Document doc;
            switch (msgId) {
                case "UploadInfoC2S":
                    name = jsonObj.getString("name");
                    String host = jsonObj.getString("host");
                    int port = jsonObj.getInt("port");
                    String status = jsonObj.getString("status");

                    // 客户端通过json送过来的name是唯一的（这个给用户看的，所以正好拿它做关键值）
                    collection = DatabaseControl.Instance().getGameServersDatabase().getCollection("basic");
                    doc = collection.find(eq("Name", name)).first();
                    if (doc == null) {
                        //为该服务器生成第一个记录
                        Document document = new Document("Name", name)
                                .append("Host", host)
                                .append("Port", port)
                                .append("Status", status);
                        collection.insertOne(document);
                    } else {
                        //更新相应记录的状态
                        Bson bson = Filters.eq("Name", name);
                        collection.updateOne(bson, Updates.set("Host", host));
                        collection.updateOne(bson, Updates.set("Port", port));
                        collection.updateOne(bson, Updates.set("Status", status));
                    }

                    //每个client应该在客户端连接上的一瞬间就创建了，这里我们给它名字
                    ClientManager.Instance().getClient(ctx.channel().id()).name = name;
                    break;
                case "OfflineReqC2S":
                    name = jsonObj.getString("name");
                    collection = DatabaseControl.Instance().getGameServersDatabase().getCollection("basic");
                    doc = collection.find(eq("Name", name)).first();
                    if (doc != null) {
                        collection.updateOne(Filters.eq("Name", name), Updates.set("Status", "offline"));
                    } else {
                        System.out.println("警告：客户端请求离线，但在数据库中找不到对应的名字.");
                    }
                    break;
                default:
                    break;
            }
        }catch (Exception e) {
            System.out.println(e.toString());
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }

    private void setClientStatus(ChannelHandlerContext ctx, String status) {
        Client client = ClientManager.Instance().getClient(ctx.channel().id());
        if (client.name != null && client.name.length() != 0) {
            String name = client.name;

            MongoCollection<Document> collection = DatabaseControl.Instance().getGameServersDatabase().getCollection("basic");
            Document doc = collection.find(eq("Name", name)).first();
            if (doc == null) {
                System.out.println("警告：需要更新一个服务器的状态，根据名字在数据库中却查不到记录。");
            } else {
                //更新相应记录的状态
                collection.updateOne(Filters.eq("Name", name), Updates.set("Status", status));
            }
        }
    }
}
